// SPDX-License-Identifier: GPL-2.0
/*
 * rtc.c - Driver of LomboTech RTC module
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/syscore_ops.h>
#include <asm/irq.h>
#include "rtc.h"

#undef MOD_NAME
#define MOD_NAME "RTC"

#ifdef CONFIG_ARCH_LOMBO_N7V1
void lombo_lfeosc_fanout_enable(u32 enable)
{
	struct rtc_device *rtc_dev = rtc_class_open(RTC_NAME);
	struct lombo_rtc *lombo_rtc = dev_get_drvdata(rtc_dev->dev.parent);
	reg_rtc_t *base = lombo_rtc->base;

	csp_rtc_lfeosc_fanout_enable(&(base->rtc_lfeosc_fanout_cfg), enable);
}
EXPORT_SYMBOL(lombo_lfeosc_fanout_enable);
#endif

static int lombo_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
{
	struct lombo_rtc *rtc = dev_get_drvdata(dev);
	reg_rtc_t *base = rtc->base;

	csp_rtc_alarm_int_enable(&(base->rtc_alarm_int_en), enabled);

	return 0;
}

static int lombo_ld_int_handler(struct lombo_rtc *rtc)
{
	u32 ld_int_pending = 0;
	u32 ld_int_enable = 0;
	u32 det_flg = 0;
	reg_rtc_t *base = rtc->base;

	ld_int_enable = csp_rtc_get_ld_int_enable(&(base->rtc_ld_en));
	ld_int_pending = csp_rtc_get_ld_int_pending
				(&(base->rtc_ld_int_pending));
	if ((ld_int_enable & ld_int_pending)) {
		det_flg = csp_rtc_get_clk_src_stat(&(base->rtc_clk_ctrl));
		if (det_flg != CLK_SRC_LFEOSC) {
			dev_warn(&(rtc->rtc_dev->dev),
		   "LEFOSC is not detected, rtc clock source switch to RCOSC");
			csp_rtc_set_clk_src(&(base->rtc_clk_ctrl),
						    CLK_SRC_RCOSC);
		}
		csp_rtc_ld_int_clr(&(base->rtc_ld_int_clr));
	}

	return 0;
}

static int lombo_alarm_int_handler(struct lombo_rtc *rtc)
{
	u32 alarm_int_pending = 0;
	u32 alarm_int_enable = 0;
	reg_rtc_t *base = rtc->base;

	alarm_int_enable = csp_rtc_get_alarm_int_enable
				(&(base->rtc_alarm_int_en));
	alarm_int_pending = csp_rtc_get_alarm_int_pending
				(&(base->rtc_alarm_int_pending));
	if ((alarm_int_enable & alarm_int_pending)) {
		rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
		csp_rtc_alarm_int_clr(&(base->rtc_alarm_int_clr));
	}

	return 0;
}

static irqreturn_t lombo_rtc_irq_handler(int irq, void *id)
{
	struct lombo_rtc *rtc = (struct lombo_rtc *)id;

	if (rtc->data->low_volt_int_handler)
		rtc->data->low_volt_int_handler(rtc);
	lombo_ld_int_handler(rtc);
	lombo_alarm_int_handler(rtc);

	return IRQ_HANDLED;
}

static int lombo_rtc_time_to_week(struct rtc_time *rtc_tm)
{
	u32 week, year, mon, day;

	year	= rtc_tm->tm_year + 1900;
	mon	= rtc_tm->tm_mon + 1;
	day	= rtc_tm->tm_mday;

	if (mon == 1 || mon == 2) {
		mon += 12;
		year--;
	}
	/* Kim larsen calculation formula to calculate week. */
	week = (day + 2 * mon + 3 * (mon + 1) / 5 +
		year + year / 4 - year / 100 + year / 400) % 7;
	/* 0 is sunday, 1 is monday, and so on. */
	week = (week + 1) % 7;

	return week;
}

static int lombo_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
	reg_rtc_rtc_ymd_t reg_ymd;
	reg_rtc_rtc_week_t reg_week;
	reg_rtc_rtc_hms_t reg_hms;
	u32 have_retried = 0;
	struct lombo_rtc *rtc = dev_get_drvdata(dev);
	reg_rtc_t *base = rtc->base;

retry_get_time:
	reg_ymd.val	= csp_rtc_get_ymd(&(base->rtc_ymd));
	reg_week.val	= csp_rtc_get_week(&(base->rtc_week));
	reg_hms.val	= csp_rtc_get_hms(&(base->rtc_hms));

	if ((0 == reg_hms.bits.second) && (have_retried == 0)) {
		have_retried = 1;
		goto retry_get_time;
	}

	rtc_tm->tm_year	= reg_ymd.bits.year - 1900;
	rtc_tm->tm_mon	= reg_ymd.bits.month - 1;
	rtc_tm->tm_mday	= reg_ymd.bits.day;
	rtc_tm->tm_wday	= reg_week.bits.week;
	rtc_tm->tm_hour	= reg_hms.bits.hour;
	rtc_tm->tm_min	= reg_hms.bits.minute;
	rtc_tm->tm_sec	= reg_hms.bits.second;

	return 0;
}

static int lombo_rtc_settime(struct device *dev, struct rtc_time *tm)
{
	u32 year = 0;
	u32 mon = 0;
	u32 day = 0;
	u32 week = 0;
	u32 hour = 0;
	u32 min = 0;
	u32 sec = 0;
	struct lombo_rtc *rtc = dev_get_drvdata(dev);
	reg_rtc_t *base = rtc->base;

	year	= tm->tm_year + 1900;
	mon	= tm->tm_mon + 1;
	day	= tm->tm_mday;
	week	= tm->tm_wday;
	hour	= tm->tm_hour;
	min	= tm->tm_min;
	sec	= tm->tm_sec;
	week = lombo_rtc_time_to_week(tm);

	csp_rtc_set_ymd(&(base->rtc_ymd), day, mon, year);
	csp_rtc_set_week(&(base->rtc_week), week);
	csp_rtc_set_hms(&(base->rtc_hms), sec, min, hour);

	return 0;
}

static int lombo_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
	int year = -1;
	int month = -1;
	int day = -1;
	int week = -1;
	int hour = -1;
	int minute = -1;
	int second = -1;
	struct rtc_time *alm_tm = &alrm->time;
	reg_rtc_rtc_alarm_hms_match_t reg_alm_hms;
	u32 alarm_match_en_stat = 0;
	struct lombo_rtc *rtc = dev_get_drvdata(dev);
	reg_rtc_t *base = rtc->base;

	alarm_match_en_stat = csp_rtc_alarm_match_get_enable
				(&(base->rtc_alarm_match_en));
	reg_alm_hms.val = csp_rtc_get_alarm_hms(&(base->rtc_alarm_hms_match));
	if (alarm_match_en_stat &  ALARM_DAY_MATCH_EN)
		day = csp_rtc_get_alarm_day(&(base->rtc_alarm_day_match));
	if (alarm_match_en_stat & ALARM_WEEK_MATCH_EN)
		week = csp_rtc_get_alarm_week(&(base->rtc_alarm_week_match));
	if (alarm_match_en_stat & ALARM_HOUR_MATCH_EN)
		hour = reg_alm_hms.bits.hour_match;
	if (alarm_match_en_stat & ALARM_MINUTE_MATCH_EN)
		minute = reg_alm_hms.bits.minute_match;
	if (alarm_match_en_stat & ALARM_SECOND_MATCH_EN)
		second = reg_alm_hms.bits.second_match;

	alm_tm->tm_year	= year;
	alm_tm->tm_mon	= month;
	alm_tm->tm_mday	= day;
	alm_tm->tm_wday	= week;
	alm_tm->tm_hour	= hour;
	alm_tm->tm_min	= minute;
	alm_tm->tm_sec	= second;

	return 0;
}

static int lombo_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
	int hour = -1;
	int minute = -1;
	int second = -1;
	u32 alarm_match_en = 0;
	struct lombo_rtc *rtc = dev_get_drvdata(dev);
	reg_rtc_t *base = rtc->base;

	alarm_match_en = csp_rtc_alarm_match_get_enable
			 (&(base->rtc_alarm_match_en));
	hour	= alrm->time.tm_hour;
	minute	= alrm->time.tm_min;
	second	= alrm->time.tm_sec;

	if (hour < 24 && hour >= 0)
		alarm_match_en |= ALARM_HOUR_MATCH_EN;
	if (minute < 60 && minute >= 0)
		alarm_match_en |= ALARM_MINUTE_MATCH_EN;
	if (second < 60 && second >= 0)
		alarm_match_en |= ALARM_SECOND_MATCH_EN;

	csp_rtc_alarm_match_enable(&(base->rtc_alarm_match_en),
				   alarm_match_en);
	csp_rtc_set_alarm_hms(&(base->rtc_alarm_hms_match),
			      hour, minute, second);

	lombo_rtc_alarm_irq_enable(dev, alrm->enabled);

	return 0;
}

static const struct rtc_class_ops lombo_rtc_ops = {
	.read_time		= lombo_rtc_gettime,
	.set_time		= lombo_rtc_settime,
	.read_alarm		= lombo_rtc_getalarm,
	.set_alarm		= lombo_rtc_setalarm,
	.alarm_irq_enable	= lombo_rtc_alarm_irq_enable,
};

static int lombo_check_rtc_time(struct lombo_rtc *rtc)
{
	struct rtc_time rtc_tm;

	if (rtc->dev == NULL) {
		PRT_ERR("can't find lombo rtc device\n");
		return -ENODEV;
	}

	lombo_rtc_gettime(rtc->dev, &rtc_tm);
	if (rtc_valid_tm(&rtc_tm)) {
		rtc_tm.tm_year	= 70;
		rtc_tm.tm_mon	= 0;
		rtc_tm.tm_mday	= 1;
		rtc_tm.tm_hour	= 0;
		rtc_tm.tm_min	= 0;
		/**
		 * the second keeps the startup time unchanged, because
		 * if the second is forceibly set to 0,it will cause the
		 * initalization of xtime to fail in hctosys, and then
		 * return 0 in cat /sys/class/rtc/rtc0/hctosys
		 **/
		rtc_tm.tm_sec	= (rtc_tm.tm_sec < 0) ? 0 : rtc_tm.tm_sec;
		rtc_tm.tm_sec	= (rtc_tm.tm_sec < 60) ? rtc_tm.tm_sec : 59;

		lombo_rtc_settime(rtc->dev, &rtc_tm);
		dev_warn(rtc->dev,
			 "warning: invalid RTC value so initializing it\n");
	}

	return 0;
}

static const struct of_device_id lombo_rtc_match[];
static const struct lombo_rtc_data *lombo_rtc_get_data(
				struct platform_device *pdev)
{
	const struct of_device_id *match;

	match = of_match_node(lombo_rtc_match, pdev->dev.of_node);
	return match->data;
}

static int lombo_rtc_remove(struct platform_device *pdev)
{
	struct lombo_rtc *rtc  = platform_get_drvdata(pdev);

#ifdef CONFIG_ARCH_LOMBO_N7V1
	iounmap(rtc->base);
#endif
	platform_set_drvdata(pdev, NULL);
	if (rtc->data->int_enable)
		rtc->data->int_enable(rtc, DISABLE);

	return 0;
}

static int lombo_rtc_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct lombo_rtc *rtc = NULL;
	struct resource *res = NULL;

	rtc = devm_kzalloc(&pdev->dev, sizeof(struct lombo_rtc), GFP_KERNEL);
	if (!rtc) {
		PRT_ERR("failed to malloc memory\n");
		return -ENOMEM;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (NULL == res) {
		dev_err(&pdev->dev, "failed to get memory region resource\n");
		return -ENOMEM;
	}

#ifdef CONFIG_ARCH_LOMBO_N7V1
	rtc->base = ioremap(res->start, resource_size(res));
	if (rtc->base == NULL) {
		dev_err(&pdev->dev, "failed ioremap()\n");
		goto err_nortc;
	}
#else
	rtc->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(rtc->base)) {
		dev_err(&pdev->dev, "failed ioremap()\n");
		return PTR_ERR(rtc->base);
	}
#endif
	/* get IRQ */
	rtc->irq_no = platform_get_irq(pdev, 0);
	if (rtc->irq_no < 0) {
		dev_err(&pdev->dev, "no irq for rtc\n");
		return rtc->irq_no;
	}
	dev_dbg(&pdev->dev, "lombo_rtc: get irq: %d\n", rtc->irq_no);

	rtc->dev = &pdev->dev;
	rtc->wake_en = DISABLE;
	rtc->data = lombo_rtc_get_data(pdev);
	if (!rtc->data) {
		dev_err(&pdev->dev, "failed getting lombo_rtc_data\n");
		return -EINVAL;
	}
	platform_set_drvdata(pdev, rtc);

	/* low voltage detect */
	if (rtc->data->low_volt_detect)
		rtc->data->low_volt_detect(rtc);
	if (rtc->data->core_config)
		rtc->data->core_config(rtc);
	if (rtc->data->clk_src_init)
		rtc->data->clk_src_init(rtc);
	device_init_wakeup(&pdev->dev, 1);
	/* enbale the wake_alarm bit */
	csp_rtc_wakeup_enable(rtc->base, 1);

	lombo_check_rtc_time(rtc);

	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, RTC_NAME,
					&lombo_rtc_ops, THIS_MODULE);
	if (IS_ERR(rtc)) {
		dev_err(&pdev->dev, "cannot attach rtc\n");
		ret = PTR_ERR(rtc);
		goto err_nortc;
	}

	ret = devm_request_irq(&pdev->dev, rtc->irq_no, lombo_rtc_irq_handler,
			  IRQF_NO_SUSPEND | IRQF_SHARED, RTC_NAME, rtc);
	if (ret) {
		dev_err(&pdev->dev, "IRQ%d error %d\n", rtc->irq_no, ret);
		goto err_nortc;
	}

	return 0;

err_nortc:
#ifdef CONFIG_ARCH_LOMBO_N7V1
	iounmap(rtc->base);
#endif
	platform_set_drvdata(pdev, NULL);
	return ret;
}

#ifdef CONFIG_PM
static int lombo_rtc_suspend(struct device *dev)
{
	struct lombo_rtc *rtc = dev_get_drvdata(dev);

	if (device_may_wakeup(dev) && !rtc->wake_en) {
		if (enable_irq_wake(rtc->irq_no) == 0)
			rtc->wake_en = true;
		else
			dev_err(dev, "enable_irq_wake failed\n");
	}

	return 0;
}

static int lombo_rtc_resume(struct device *dev)
{
	struct lombo_rtc *rtc = dev_get_drvdata(dev);

	if (device_may_wakeup(dev) && rtc->wake_en) {
		disable_irq_wake(rtc->irq_no);
		rtc->wake_en = false;
	}

	return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(lombo_rtc_pm_ops, lombo_rtc_suspend,
						lombo_rtc_resume);

static int lombo_rtc_low_volt_detect(struct lombo_rtc *rtc)
{
#ifndef CONFIG_ARCH_LOMBO_N7V1
	u32 low_vol_stat = 0;
	reg_rtc_t *base = rtc->base;

	low_vol_stat = csp_rtc_get_low_vol_stat(&(base->rtc_clk_ctrl));
	if (low_vol_stat != 0) {
		csp_rtc_rst(&(base->rtc_rst));
		PRT_WARN("reset rtc\n");
		lombo_check_rtc_time(rtc);
		low_vol_stat = csp_rtc_get_low_vol_stat(&(base->rtc_clk_ctrl));
		if (low_vol_stat != 0) {
			dev_err(&(rtc->rtc_dev->dev),
			"low voltage detected, date/time is not reliable\n");
		}
	}
#endif
	return 0;
}

static int lombo_low_volt_int_handler(struct lombo_rtc *rtc)
{
	u32 low_volt_int_pending = 0;
	u32 low_volt_int_enable = 0;
	reg_rtc_t *base = rtc->base;

	low_volt_int_pending = csp_rtc_get_volt_dete_int_pending
				(&(base->rtc_ld_int_pending));
	low_volt_int_enable = csp_rtc_get_volt_dete_int_en
				(&(base->rtc_ld_int_en));
	if ((low_volt_int_enable & low_volt_int_pending)) {
		if (rtc->data->low_volt_detect)
			rtc->data->low_volt_detect(rtc);
	}

	return 0;
}

static int n7v3_rtc_core_config(struct lombo_rtc *rtc)
{
	reg_rtc_t *base = rtc->base;

	csp_rtc_core_config(&base->rtc_core);

	return 0;
}

/* except alarm interuppt  */
static int lombo_int_enable(struct lombo_rtc *rtc, u32 enable)
{
	reg_rtc_t *base = rtc->base;

	/**
	 * NOTE: lfosc auto detect mode is not recommend to use.
	 * so we switch to manual select(default).close lefosc
	 * detect int.
	 */
	csp_rtc_ld_enable(&(base->rtc_ld_en), enable);
	/* csp_rtc_ld_int_enable(&(base->rtc_ld_int_en), enable); */

	return 0;
}

static int lombo_clr_all_pending(struct lombo_rtc *rtc)
{
	reg_rtc_t *base = rtc->base;

	csp_rtc_ld_int_clr(&(base->rtc_ld_int_clr));
	csp_rtc_alarm_int_clr(&(base->rtc_alarm_int_clr));

	return 0;
}

static int n7v5_n5v1_int_enable(struct lombo_rtc *rtc, u32 enable)
{
	reg_rtc_t *base = rtc->base;

	/**
	 * NOTE: lfosc auto detect mode is not recommend to use.
	 * so we switch to manual select(default).close lefosc
	 * detect int.
	 */
	csp_rtc_ld_enable(&(base->rtc_ld_en), enable);
	/* csp_rtc_ld_int_enable(&(base->rtc_ld_int_en), enable); */
	csp_rtc_alarm_int_enable(&(base->rtc_alarm_int_en), enable);
	csp_rtc_volt_dete_int_en(&(base->rtc_ld_int_en), enable);

	return 0;
}

static int n9v3_int_enable(struct lombo_rtc *rtc, u32 enable)
{
	return n7v5_n5v1_int_enable(rtc, enable);
}

static int n7v5_n5v1_clr_all_pending(struct lombo_rtc *rtc)
{
	reg_rtc_t *base = rtc->base;

	csp_rtc_ld_int_clr(&(base->rtc_ld_int_clr));
	csp_rtc_alarm_int_clr(&(base->rtc_alarm_int_clr));
	csp_rtc_volt_dete_int_clr(&(base->rtc_ld_int_clr));

	return 0;
}

static int n9v3_clr_all_pending(struct lombo_rtc *rtc)
{
	return n7v5_n5v1_clr_all_pending(rtc);
}

static int lombo_rtc_clk_src_init(struct lombo_rtc *rtc)
{
#ifndef CONFIG_ARCH_LOMBO_N7V1
	reg_rtc_t *base = rtc->base;

	if (rtc->data->clr_all_pending)
		rtc->data->clr_all_pending(rtc);

	if (rtc->data->int_enable)
		rtc->data->int_enable(rtc, ENABLE);

#if defined FPGA
	csp_rtc_clk_src0_enable(&(base->rtc_clk_ctrl), ENABLE);
	csp_rtc_set_clk_src0(&(base->rtc_clk_ctrl), RTC_SRC);
#else
	/**
	 * NOTE: lfosc auto detect mode is not recommend to use.
	 * so we switch to manual select(default).
	 */
	csp_rtc_set_clk_src(&(base->rtc_clk_ctrl), CLK_SRC0);
	csp_rtc_clk_src0_enable(&(base->rtc_clk_ctrl), ENABLE);
	if (csp_rtc_get_ld_det_flg(&(base->rtc_ld_int_pending))
					== CLK_SRC_LFEOSC)
		csp_rtc_set_clk_src0(&(base->rtc_clk_ctrl), CLK_SRC_LFEOSC);
	else {
		csp_rtc_set_clk_src0(&(base->rtc_clk_ctrl), CLK_SRC_RCOSC);
		dev_err(rtc->dev,
		  "LEFOSC is not detected, rtc clock source switch to RCOSC");
	}
#endif
	dev_dbg(rtc->dev, "lombo_rtc clock source is %s\n",
				(RTC_SRC ? "LFEOSC" : "RCOSC"));

#endif
	return 0;
}

static int n7v1_rtc_clk_src_init(struct lombo_rtc *rtc)
{
	u32 clk_src_stat = 0;
	reg_rtc_t *base = rtc->base;

	/* lombo_rtc_ld_int_enable(ENABLE); */
	/* get RTC CLK-SRC and set to RTC_SRC */
	clk_src_stat = csp_rtc_get_clk_src_stat(&(base->rtc_clk_ctrl));
	if (clk_src_stat != RTC_SRC)
		csp_rtc_set_clk_src0(&(base->rtc_clk_ctrl), RTC_SRC);

	if (rtc->data->clr_all_pending)
		rtc->data->clr_all_pending(rtc);

	if (rtc->data->int_enable)
		rtc->data->int_enable(rtc, ENABLE);
	dev_dbg(rtc->dev, "lombo_rtc clock source is %s\n",
				(RTC_SRC ? "LFEOSC" : "RCOSC"));

	return 0;
}

static struct lombo_rtc_data const n7v1_rtc_data = {
	.low_volt_detect = NULL,
	.low_volt_int_handler = NULL,
	.core_config = NULL,
	.int_enable = lombo_int_enable,
	.clr_all_pending = lombo_clr_all_pending,
	.clk_src_init = n7v1_rtc_clk_src_init,
};

static struct lombo_rtc_data const n7v3_rtc_data = {
	.low_volt_detect = lombo_rtc_low_volt_detect,
	.low_volt_int_handler = NULL,
	.core_config = n7v3_rtc_core_config,
	.int_enable = lombo_int_enable,
	.clr_all_pending = lombo_clr_all_pending,
	.clk_src_init = lombo_rtc_clk_src_init,
};

static struct lombo_rtc_data const n7v5_n5v1_rtc_data = {
	.low_volt_detect = lombo_rtc_low_volt_detect,
	.low_volt_int_handler = lombo_low_volt_int_handler,
	.core_config = NULL,
	.int_enable = n7v5_n5v1_int_enable,
	.clr_all_pending = n7v5_n5v1_clr_all_pending,
	.clk_src_init = lombo_rtc_clk_src_init,
};

static struct lombo_rtc_data const n9v1_rtc_data = {
	.low_volt_detect = lombo_rtc_low_volt_detect,
	.low_volt_int_handler = NULL,
	.core_config = NULL,
	.int_enable = lombo_int_enable,
	.clr_all_pending = lombo_clr_all_pending,
	.clk_src_init = lombo_rtc_clk_src_init,
};

static struct lombo_rtc_data const n9v3_rtc_data = {
	.low_volt_detect = lombo_rtc_low_volt_detect,
	.low_volt_int_handler = lombo_low_volt_int_handler,
	.core_config = NULL,
	.int_enable = n9v3_int_enable,
	.clr_all_pending = n9v3_clr_all_pending,
	.clk_src_init = lombo_rtc_clk_src_init,
};

static const struct of_device_id lombo_rtc_match[] = {
	{
		.compatible = "lombo,n5v1-rtc",
		.data = &n7v5_n5v1_rtc_data,
	},
	{
		.compatible = "lombo,n7v1-rtc",
		.data = &n7v1_rtc_data,
	},
	{
		.compatible = "lombo,n7v3-rtc",
		.data = &n7v3_rtc_data,
	},
	{
		.compatible = "lombo,n7v5-rtc",
		.data = &n7v5_n5v1_rtc_data,
	},
	{
		.compatible = "lombo,n9v1-rtc",
		.data = &n9v1_rtc_data,
	},
	{
		.compatible = "lombo,n9v3-rtc",
		.data = &n9v3_rtc_data,
	},
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, lombo_rtc_match);

static struct platform_driver lombo_rtc_driver = {
	.probe		= lombo_rtc_probe,
	.remove		= lombo_rtc_remove,
	.driver		= {
		.name	= "lombo_rtc",
		.owner	= THIS_MODULE,
		.pm	= &lombo_rtc_pm_ops,
		.of_match_table	= lombo_rtc_match,
	},
};

static int __init lombo_rtc_init(void)
{
	return platform_driver_register(&lombo_rtc_driver);
}

static void __exit lombo_rtc_exit(void)
{
	platform_driver_unregister(&lombo_rtc_driver);
}

module_init(lombo_rtc_init);
module_exit(lombo_rtc_exit);

MODULE_AUTHOR("lomboswer@lombotech.com");
MODULE_DESCRIPTION("Driver for LomboTech RTC module");
MODULE_LICENSE("GPL");
